from random import randint
import re

from django.urls import reverse

from users.models import User
import view as view
from django.shortcuts import render, redirect
from django.http.response import HttpResponseBadRequest, HttpResponse, JsonResponse

from libs.captcha.captcha import captcha
from django_redis import get_redis_connection
from utils.response_code import RETCODE
from libs.yuntongxun.sms import CCP
import logging

logger = logging.getLogger('django')
from django.db import DatabaseError

# Create your views here.

# 注册试图
from django.views import View


class RegisterView(View):

    def get(self, request):
        return render(request, 'register.html')

    def post(self, request):
        '''
        1.接受数据
        2.验证数据
          2.1 参数是否齐全
          2.2 手机号的格式是否正确
          2.3 密码是否符合格式
          2.4 密码和确人密码是否一致
          2.5 短信验证码是否与redis中一致
        3.保存注册数据
        4.返回相应条状到指定页面

        '''
        # 1.接受数据
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        smscode = request.POST.get('sms_code')
        # 2.验证数据
        #   2.1 参数是否齐全
        if not all([mobile, password, password2, smscode]):
            return HttpResponseBadRequest('缺少必要的参数')

        #   2.2 手机号的格式是否正确 (正则)
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('手机号不符合规则')

        #   2.3 密码是否符合格式
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseBadRequest('请输入8-20为密码,密码是数字,字母')

        #   2.4 密码和确人密码是否一致
        if password != password2:
            return HttpResponseBadRequest('两次密码不一致')

        #   2.5 短信验证码是否与redis中一致
        redis_conn = get_redis_connection('default')
        redis_sms_code = redis_conn.get('sms:%s' % mobile)
        if redis_sms_code is None:
            return HttpResponseBadRequest('短信验证码已过期')
        if smscode != redis_sms_code.decode():  # Python decode() 方法以 encoding 指定的编码格式解码字符串。默认编码为字符串编码。
            return HttpResponseBadRequest('短信验证码不一致')
        # 3.保存注册数据
        # create_user() 可以使用系统的方法来对用户信息加密
        try:
            user = User.objects.create_user(username=mobile, mobile=mobile, password=password)
        except DatabaseError as e:
            logger.error(e)
            return HttpResponseBadRequest('注册失败')

        # 实现login session 信息 状态保存信息
        from django.contrib.auth import login
        login(request, user)
        # 设置cookie信息, 以方便首页中 用户信息展示的判断和用户信息的展示
        response = redirect(reverse('home:index'))
        response.set_cookie('is_login', True)
        response.set_cookie('username', user.username, max_age=7 * 24 * 3600)
        return response
        # # 4.返回相应条状到指定页面
        # # 展示返回一个注册成功的信息 后期在实现跳转到指定页面
        # # return HttpResponse('注册成功, 重新定向到首页')
        # #redirect 时进行重定向
        # #reverse 时可以通过 namespace:name 来获取到试图对应的路由
        # return redirect(reverse('home:index'))


# 图片验证码
class ImageCodeView(View):
    def get(self, request):
        """

        1.接受 uuid

        2.判断uuid是否获取到
        3.通过调用captcha 来生成图片验证码(图片二进制和图片内容)
        4.将 图片内容存放到redis中
          uuid 作为key  图片内容作为一个value 同时我们还要设置一个实效
        5.返回图片二进制到前端
        """
        # """
        #       ''''''
        #       1.接受 uuid 由js生成
        uuid = request.GET.get('uuid')

        #       2.判断uuid是否获取到
        if uuid is None:
            return HttpResponseBadRequest('没有传递uuid')
        #       3.通过调用captcha 来生成图片验证码(图片二进制和图片内容)
        text, image = captcha.generate_captcha()
        #       4.将 图片内容存放到redis中
        redis_connection = get_redis_connection('default')
        # key 设置为 uuid
        # second 过期秒数 300s
        # value text
        redis_connection.setex('img:%s' % uuid, 300, text)

        #         uuid 作为key  图片内容作为一个value 同时我们还要设置一个实效
        #       5.返回图片二进制到前端

        return HttpResponse(image, content_type='image/jpeg')
        #       """

    pass


# 短信验证码
class SmsCodeView(View):

    def get(self, request):
        """
        1.接受参数 (查询字符串的形式传递过来)

        2.参数的验证
          2.1 验证参数是否齐全  (mobile image_code uuid)
          2.2 图片验证码的验证
           链接redis 获取redis中的图片验证码
           判断图片验证码是否存在
           如果图片验证码未过期 我们获取之后就可以删除图片验证码
           比对图片验证码(大小写问题)

        3.生成短信验证码
        4.将短信验证码保存到redis中
        5.发送短信
        6.返回响应

        """
        #         1.接受参数 (查询字符串的形式传递过来) 前端输入的
        mobile = request.GET.get('mobile')
        image_code = request.GET.get('image_code')
        uuid = request.GET.get('uuid')

        #
        # 2.参数的验证

        #           2.1 验证参数是否齐全  (mobile image_code uuid)
        if not all([mobile, image_code, uuid]):
            return JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsy': '缺少必要的参数'})
        #           2.2 图片验证码的验证
        #            链接redis 获取redis中的图片验证码
        redis_conn = get_redis_connection('default')
        redis_image_code = redis_conn.get('img:%s' % uuid)  # 用get取对应 uuid下的text
        #            判断图片验证码是否存在
        if redis_image_code is None:
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图片验证码已过期'})
        #            如果图片验证码未过期 我们获取之后就可以删除图片验证码
        try:
            redis_conn.delete('img:%s' % uuid)
        except Exception as e:
            logger.error(e)

        #            比对图片验证码(大小写问题, redis的数据时bytes类型)
        if redis_image_code.decode().lower() != image_code.lower():
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图片验证码错误'})

        #
        #         3.生成短信验证码
        sms_code = '%06d' % randint(0, 999999)
        # 为了后期比对方便 把短信验证码保存到 日志文件中
        logger.info(sms_code)

        #         4.将短信验证码保存到redis中
        redis_conn.setex('sms:%s' % mobile, 300, sms_code)

        #         5.发送短信
        CCP().send_template_sms(mobile, [sms_code, 5], 1)
        #         6.返回响应
        return JsonResponse({'code': RETCODE.OK, 'errmsy': '短信验证码发送成功'})


class LoginView(View):

    def get(self, request):

        return render(request, 'login.html')

    def post(self, request):
        '''
               # 1.接受参数
               # 2.参数的验证
               #   2.2 验证手机号是否符合规则
               #   2.3 验证密码是否符合规则
               # 3.用户认证登录
               # 4.状态的保持
               # 5.根据用户选择的是否记住登录状态来进行判断
               # 6.为了首页显示我们需要设置一些cookie信息
               # 7.返回响应



               '''
        # 1.接受参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        remember = request.POST.get('remember')

        # 2.参数的验证

        #   2.2 验证手机号是否符合规则
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('手机号不符合规则')
        #   2.3 验证密码是否符合规则
        if not re.match(r'^[a-zA-Z0-9]{8,20}$', password):
            return HttpResponseBadRequest('密码不符合规则')
        # 3.用户认证登录
        # 采用系统自带的认证方法进行认证
        # 如果用名和密码正确,会返回user
        # 弱国用户名密码不正确,会返回None
        from django.contrib.auth import authenticate
        # 默认的认证方法时 针对 username 字段进行用户名的判断
        # 当前的判断信息时 手机号,所以需要修改一下认证字段
        # 需要到user模型中进行修改等测试出现问题再修改
        user = authenticate(mobile=mobile, password=password)  # 这个authenticate 能链接user中数组库

        if user is None:
            return HttpResponseBadRequest('用户名或密码错误')
        # 4.状态的保持
        from django.contrib.auth import login

        login(request, user)
        # response = redirect(reverse('home:index'))
        # 5.根据用户选择的是否记住登录状态来进行判断
        # 根据next参数进行页面跳转  因为有可能是 非法方位被重定向过来的 就要让他正确登陆后直接去该去的地方
        next_page = request.GET.get('next')
        if next_page:
            response = redirect(next_page)
        else:
            response = redirect(reverse('home:index'))

        if remember != 'on':
            request.session.set_expiry(0)
            response.set_cookie('is_login', True)
            response.set_cookie('username', user.username, max_age=14 * 24 * 3600)

        else:  # 默认记住两周
            request.session.set_expiry(None)
            response.set_cookie('is_login', True, max_age=14 * 24 * 3600)
            response.set_cookie('username', user.username, max_age=14 * 24 * 3600)

        # 6.为了首页显示我们需要设置一些cookie信息

        # 7.返回响应
        return response


from django.contrib.auth import logout


class LogoutView(View):

    def get(self, request):
        # 1.session 数据清除

        logout(request)
        # 2.删除部分cookie数据
        response = redirect(reverse('home:index'))

        response.delete_cookie('is_login')

        # 3.跳转到首页
        return response


class ForgetPasswordView(View):

    def get(self, request):
        return render(request, 'forget_password.html')

    def post(self, request):
        '''
        1.接收数据
        2.数据验证
          2.1 判断参数是否齐全
          2.2 手机号是否符合规则
          2.3 密码是否符合规则
          2.4 判断密码与确认密码是否一致
          2.5 判断短信验证吗是否正确
        3.根据手机号进行用户数据的查询
        4.如果手机号查询出用户信息 则进行用户信息密码的修改
        5.如果没有查询到到用户信息  则进行新用户的用户注册
        6.进行页面跳转, 跳转到新用户的创建
        7.返回响应
        '''
        # 1.接收数据
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        smscode = request.POST.get('sms_code')
        # 2.数据验证
        #   2.1 判断参数是否齐全
        if not all([mobile, password, password2, smscode]):
            return HttpResponseBadRequest('参数不全')
        #   2.2 手机号是否符合规则
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('密码不符合规则')
        #   2.3 密码是否符合规则
        if not re.match(r'^[A-Za-z0-9]{8,20}$', password):
            return HttpResponseBadRequest('密码不符合规则')
        #   2.4 判断密码与确认密码是否一致
        if password != password2:
            return HttpResponseBadRequest('密码不一致')
        #   2.5 判断短信验证吗是否正确
        redis_conn = get_redis_connection('default')
        redis_sms_code = redis_conn.get('sms:%s' % mobile)
        if redis_sms_code is None:
            return HttpResponseBadRequest('短信验证码已过期')
        if redis_sms_code.decode() != smscode:
            return HttpResponseBadRequest('短信验证码错误')

        # 3.根据手机号进行用户数据的查询
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:

            # 5.如果没有查询到到用户信息  则进行新用户的用户注册
            try:
                User.objects.create_user(username=mobile, mobile=mobile, password=password)
            except Exception:
                return HttpResponseBadRequest('修改失败,请稍后再试')

        else:
            # 4.如果手机号查询出用户信息 则进行用户信息密码的修改
            user.set_password(password)
            user.save()

        # 6.进行页面跳转, 跳转到新用户的创建
        response = redirect(reverse('users:login'))
        # 7.返回响应
        return response


# 导入判定登录状态的包  给UserCenterView 继承
# 入股用户未登录的话 则会进行默认的跳转
# 默认跳转链接是/accounts/login/?next=xxx
from django.contrib.auth.mixins import LoginRequiredMixin


class UserCenterView(LoginRequiredMixin, View):

    def get(self, request):
        # 获取登陆用户的信息
        user = request.user
        # 组织获取用户的信息
        context = {
            'username': user.username,
            'mobile': user.mobile,
            'avatar': user.avatar.url if user.avatar else None,
            'user_desc': user.user_desc
        }
        return render(request, 'center.html', context=context)

    def post(self, request):
        """
        1.接收参数
        2.将参数保存起来
        3.更新cookie中的username信息
        4.刷新当前页面(重定向操作)
        5.返回响应
        """
        user = request.user
        # 1.接收参数
        username = request.POST.get('username', user.username)
        user_desc = request.POST.get('desc', user.user_desc)
        avatar = request.FILES.get('avatar')

        # 2.将参数保存起来
        try:
            user.username = username
            user.user_desc = user_desc
            if avatar:
                user.avatar = avatar
            user.save()

        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('修改失败,请稍后再试')

        # 3.更新cookie中的username信息

        # 4.刷新当前页面(重定向操作)
        response = redirect(reverse('users:center'))
        response.set_cookie('username', user.username, max_age=14 * 24 * 3600)
        # 5.返回响应
        return response


from home.models import ArticleCategory, Article


class WriteBlogView(LoginRequiredMixin, View):

    def get(self, request):
        # 查询所有分类模型

        categories = ArticleCategory.objects.all()

        context = {
            'categories': categories

        }
        return render(request, 'write_blog.html', context=context)

    def post(self, request):
        """
        1.接受数据

        2.验证数据
        3.数据入库
        4.跳转到指定页面(暂时设为首页)

        """

        # 1.接受数据
        avatar = request.FILES.get('avatar')
        title = request.POST.get('title')
        category_id = request.POST.get('category')
        tags = request.POST.get('tags')
        sumary = request.POST.get('sumary')
        content = request.POST.get('content')
        user = request.user

        # 2.验证数据
        # 2.1 验证参数是否齐全
        if not all([title, category_id, sumary, content]):
            return HttpResponseBadRequest('参数不全')
        try:

            category = ArticleCategory.objects.get(id=category_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseBadRequest('没有此分类')

        # 3.数据入库
        try:
            article = Article.objects.create(
                author=user,
                category=category,
                avatar=avatar,
                tags=tags,
                summary=sumary,
                content=content,
                title=title,
            )

        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('发送失败,请稍后再试')
        # 4.跳转到指定页面(暂时设为首页)
        return redirect(reverse('home:index'))
